package com.boarsoft.flow.demo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.flow.core.SimpleFlow;
import com.boarsoft.flow.core.SimpleFlowEngine;
import com.boarsoft.flow.core.SimpleFlowEngineImpl;
import com.boarsoft.flow.core.bean.BaseNode;
import com.boarsoft.flow.demo.bean.User;
import com.boarsoft.flow.demo.login.LoginHard;
import com.boarsoft.flow.event.SimpleFlowListener;
import com.boarsoft.flow.event.SimpleFlowListenerImpl;

public class Main {
	private static final Logger log = LoggerFactory.getLogger(Main.class);
	private static ClassPathXmlApplicationContext ctx;

	public static void test10(SimpleFlowEngine fe) throws Throwable {
		long s = System.nanoTime();
		SimpleFlow f = fe.create("test10");
		f.setId(String.valueOf(s));
		f.setData(new ArrayList<String>());
		// fe.start(f);
		Future<SimpleFlow> ft = fe.submit(f);
		ft.get().getData();
		System.out.println(System.nanoTime() - s);
	}
	
	public static void main(String[] args) throws Throwable {
		// System.out.println(Calendar.getInstance().get(Calendar.YEAR));
		
		ctx = new ClassPathXmlApplicationContext("classpath:conf/context.xml");
		System.out.println("Startup ".concat(ctx.isRunning() ? "successfully." : "failed."));

		SimpleFlowEngine fe = (SimpleFlowEngine) ctx.getBean("simpleFlowEngine");
//		Map<String, Object> m = new HashMap<>();
//		m.put("a.b", "1000");
//		fe.start("mvel", m);
		
		Map<String, Object> pm = new HashMap<String, Object>();
		pm.put("userId", "001");
		pm.put("work", 3);
		fe.start("forkTest", pm);
		
//		fe.start("submit1", null);
//		log.info("submit1 return");
	}

	public static void main2(String[] args) throws Throwable {
		ctx = new ClassPathXmlApplicationContext("classpath:conf/context.xml");
		System.out.println("Startup ".concat(ctx.isRunning() ? "successfully." : "failed."));

		SimpleFlowEngine fe = (SimpleFlowEngine) ctx.getBean("simpleFlowEngine");
		
		
		for (int i = 0; i < 5; i++) {
			test10(fe);
//			SimpleFlow f = fe.create("test10");
//			f.setId(String.valueOf(System.currentTimeMillis()));
//			f.setData(new ArrayList<String>());
//			fe.start(f);
//			System.out.println(f.getData());
		}

		AtomicLong total = new AtomicLong(0L);
//
		long start1 = System.currentTimeMillis();
		for (int i = 0; i < 1000; i++) {
			try {
				SimpleFlow f = fe.create("test10");
				f.setId(String.valueOf(System.currentTimeMillis()));
				f.setData(new ArrayList<String>());
				// fe.start(f);
				Future<SimpleFlow> ft = fe.submit(f);
				ft.get().getData();
			} catch (Throwable e1) {
				e1.printStackTrace();
			}
		}
		System.out.print("==1==");
		System.out.println((System.currentTimeMillis() - start1) / 1000f);

		// long start2 = System.currentTimeMillis();
		// for (int i = 0; i < 1000; i++) {
		// try {
		// SimpleFlow f = fe.create("test10");
		// f.setId(String.valueOf(s));
		// fe.start(f);
		// } catch (Throwable e1) {
		// e1.printStackTrace();
		// }
		// }
		// System.out.print("==2==");
		// System.out.println((System.currentTimeMillis() - start2)/1000f);

		int threads = 200, count = 20000;
		ExecutorService es = Executors.newFixedThreadPool(threads);

		CountDownLatch cdl1 = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			es.execute(new Runnable() {
				@Override
				public void run() {
					try {
						long s = System.currentTimeMillis();
						SimpleFlow f = fe.create("test10");
						f.setId(String.valueOf(s));
						f.setData(new ArrayList<String>());
						// fe.start(f);
						Future<SimpleFlow> ft = fe.submit(f);
						ft.get().getData();
						// System.out.println(f.getData());
						total.addAndGet(System.currentTimeMillis() - s);
					} catch (Throwable e) {
						e.printStackTrace();
					} finally {
						cdl1.countDown();
					}
				}
			});
		}
		cdl1.await();
		System.out.print("==3==");
		System.out.println(total.get() * 1.0f / count);

		// total.set(0L);
		// CountDownLatch cdl2 = new CountDownLatch(count);
		// long start4 = System.currentTimeMillis();
		// for (int i = 0; i < count; i++) {
		// es.execute(new Runnable() {
		// @Override
		// public void run() {
		// try {
		// long s = System.currentTimeMillis();
		// SimpleFlow f = fe.create("test10");
		// f.setId(String.valueOf(s));
		// fe.start(f);
		// total.addAndGet(System.currentTimeMillis() - s);
		// } catch (Throwable e) {
		// e.printStackTrace();
		// } finally {
		// cdl2.countDown();
		// }
		// }
		// });
		// }
		// cdl2.await();
		// System.out.print("==4==");
		// System.out.println(System.currentTimeMillis() - start4);
		// System.out.println(total.get()/400000f);

		// final LoginHard lh = new LoginHard(threadPool);
		// final User u = new User();
		// u.setName("Mac_J");
		// u.setPassword("123");
		//
		// try {
		// long l = System.currentTimeMillis();
		// SimpleFlow fo = fe.start("login", u);
		// log.info("Flow trace {}:\n{}\n", fo.getId(),
		// JsonUtil.from(fo.getTrace()));
		// System.out.println(String.format("Flow engine take %dms",
		// System.currentTimeMillis() - l));
		// //
		// l = System.currentTimeMillis();
		// Future<Long> ft = es.submit(new Callable<Long>() {
		// @Override
		// public Long call() throws Exception {
		// return lh.start(u);
		// }
		// });
		// ft.get(3000L, TimeUnit.MILLISECONDS);
		// System.out.println(String.format("Hard code take %dms",
		// System.currentTimeMillis() - l));
		// } catch (Throwable e) {
		// log.error("Error on call LoginHard.start()", e);
		// }

		try {
			// basicTest1(fe);
			// // 由tc个线程发起压力，每个压力线程串行执行len次，每次执行都通过threadPool在业务线程中执行，并由流程引擎负责计时
			// flowPerfTest1(fe, u, es, threadPool, tc, len);
			// //
			// 由tc个线程发起压力，每个压力线程串行执行len次，每次执行都通过threadPool在业务线程中执行，并用Future处理超时
			// hardPerfTest1(lh, u, es, threadPool, tc, len);
			// // 由主线程调用流程引擎发起tc*len个流程，流程引擎在600大小的固定线程池上执行这些流程，并等待所有流程执行完毕后结束
			// flowPerfTest2(fe, u, tc, len);
			// // 由主线程通过600大小的固定线程池发起tc*len次调用，并在所有调用执行完成后结束
			// hardPerfTest2(lh, u, threadPool, tc, len);
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			es.shutdown();
			if (ctx != null) {
				ctx.close();
			}
		}
		System.exit(0);
	}

	public static void hardPerfTest2(final LoginHard lh, final User u, final ExecutorService threadPool, final int tc,
			final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		for (int n = 0; n < tc * len; n++) {
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					try {
						lh.start(u);
					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally {
						cdl.countDown();
					}
				}
			});
		}
		cdl.await();
		System.out.println(String.format("Hard code take %dms", System.currentTimeMillis() - l));
	}

	public static void flowPerfTest2(final SimpleFlowEngine fe, final User u, final int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		SimpleFlowEngineImpl fei = (SimpleFlowEngineImpl) fe;
		fei.setListener(new SimpleFlowListener() {

			@Override
			public void onError(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onSuspend(SimpleFlow flow) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onStop(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onTimeout(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onBegin(SimpleFlow flow) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onEnd(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onBeforeStep(SimpleFlow flow, Object data, BaseNode curr) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onBeforeHandle(SimpleFlow flow, Object data, Throwable throwable, Object dd) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onAfterHandle(SimpleFlow flow, Object data, Throwable throwable, Object checkInData,
					Object dataCheckOut, Throwable handleEx) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onFailHandle(SimpleFlow flow, Object data, Throwable throwable, Object checkInData,
					Throwable handleEx) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onAfterStep(SimpleFlow flow, Object data, Throwable flowEx, String next, Throwable stepEx, long time) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onFailStep(SimpleFlow flow, Object data, Throwable flowEx, String step, Throwable stepEx) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onFinally(SimpleFlow flow) {
				// TODO Auto-generated method stub
				
			}
		});

		long l = System.currentTimeMillis();
		for (int i = 0; i < tc * len; i++) {
			fe.execute("login", null, u, null);
		}
		cdl.await();
		System.out.println(String.format("Flow engine take %dms", System.currentTimeMillis() - l));

		fei.setListener(new SimpleFlowListenerImpl());
	}

	public static void flowPerfTest1(final SimpleFlowEngine fe, final User u, final ExecutorService es,
			final ExecutorService threadPool, int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		try {
			for (int n = 0; n < tc; n++) {
				es.execute(new Runnable() {
					public void run() {
						for (int i = 0; i < len; i++) {
							try {
								fe.start("login", u);
							} catch (Throwable e) {
								log.error("Error on call flow login", e);
							}
							// try {
							// Future<Long> ft = threadPool.submit(new
							// Callable<Long>() {
							// @Override
							// public Long call() throws Exception {
							// SimpleFlow f;
							// try {
							// f = fe.start("login", u);
							// return 0L; // TODO
							// } catch (Throwable e) {
							// e.printStackTrace();
							// }
							// return -1L;
							// }
							// });
							// ft.get(3000L, TimeUnit.MILLISECONDS);
							// } catch (InterruptedException |
							// ExecutionException | TimeoutException e) {
							// e.printStackTrace();
							// }
						}
						cdl.countDown();
					}
				});
			}
			cdl.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("Flow engine take %dms", System.currentTimeMillis() - l));
	}

	public static void hardPerfTest1(final LoginHard lh, final User u, final ExecutorService es,
			final ExecutorService threadPool, int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		try {
			for (int n = 0; n < tc; n++) {
				es.execute(new Runnable() {
					public void run() {
						for (int i = 0; i < len; i++) {
							try {
								lh.start(u);
								// Future<Long> ft = threadPool.submit(new
								// Callable<Long>() {
								// @Override
								// public Long call() throws Exception {
								// return lh.start(u);
								// }
								// });
								// ft.get(3000L, TimeUnit.MILLISECONDS);
							} catch (Throwable e) {
								log.error("Error on call LoginHard.start()", e);
							}
						}
						cdl.countDown();
					}
				});
			}
			cdl.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("Hard code take %dms", System.currentTimeMillis() - l));
	}

	public static void basicTest1(SimpleFlowEngine fe) throws Throwable {
		User u = new User();
		u.setName("Mac_J");
		u.setPassword("123");
		SimpleFlow f1 = fe.start("login", u);
		log.info(JsonUtil.from(f1.getData()));

		// SimpleFlow f2 = fe.start("accountQry",
		// "00000000000000000000000000000000");
		// System.out.println(JsonUtil.from(f.getData()));

		Map<String, Object> pm = new HashMap<String, Object>();
		pm.put("userId", u.getId());
		pm.put("work", 3);
		SimpleFlow f3 = fe.start("makeMoney", pm);
		System.out.println(JsonUtil.from(f3.getData()));
		System.out.println(JsonUtil.from(f3.getTrace()));
	}
}
